/* hal_i2c.c */
#include "hal_i2c.h"
#include "hal_clock.h"

void I2C_ClearRxFifo(I2C_Type *I2Cx);
void I2C_ClearTxFifo(I2C_Type *I2Cx);
void I2C_SetTxThreshold(I2C_Type *I2Cx, uint32_t num);
void I2C_SetRxThreshold(I2C_Type *I2Cx, uint32_t num);
void I2C_SetCommFreq(I2C_Type *I2Cx, uint32_t freq_hz, uint32_t clksrc_hz);

bool I2C_InitMaster(I2C_Type *I2Cx, I2C_Master_Init_Type *init)
{
    /* clear everything out before initializing the peripheral in clock_init.c. */

#if 1
    /* mxc_i2c_recover() ??? */
    if ( !I2C_Recover(I2Cx, 16u) )
    {
        return false;
    }
#endif
    
    /* enable i2c module. */
    I2Cx->CTRL |= I2C_CTRL_EN_MASK;


    I2C_ClearRxFifo(I2Cx);        //MXC_I2C_ClearRXFIFO().
    I2C_ClearTxFifo(I2Cx);        //MXC_I2C_ClearTXFIFO().
    // Set the thresholds here and allow the user to change them as needed
    //I2C_SetTxThreshold(I2Cx, 2u);
    //I2C_SetRxThreshold(I2Cx, 6u);

    /* setup freq. */
    I2C_SetCommFreq(I2Cx, init->CommClkHz, init->ClkSrcHz);
    I2C_SetTxThreshold(I2Cx, init->TxFifoThreshold);
    I2C_SetRxThreshold(I2Cx, init->RxFifoThreshold);

    /* enable master mode. */
    I2Cx->CTRL |= I2C_CTRL_MST_MODE_MASK;

    return true;
}

/* some registers might not be set with the right value after writen. */
bool I2C_Recover(I2C_Type *I2Cx, uint32_t retries)
{
    bool err = false;
    bool ctrl_bb = false;

    /* enable i2c module. */
    I2Cx->CTRL |= I2C_CTRL_EN_MASK;

    /* control the i2c module by software, and restore the previous setting later. */
    ctrl_bb = ((I2Cx->CTRL & I2C_CTRL_BB_MODE_MASK) != 0u);
    I2Cx->CTRL |= I2C_CTRL_BB_MODE_MASK;

    for (uint32_t i = 0u; i < retries; i++)
    {
        /* try to set scl=0. */
        CLOCK_DelayUs(10u);
        I2Cx->CTRL &= ~I2C_CTRL_SCL_OUT_MASK;
        CLOCK_DelayUs(10u);
        if (I2Cx->CTRL & I2C_CTRL_SCL_MASK)
        {
            I2Cx->CTRL |= I2C_CTRL_SCL_OUT_MASK | I2C_CTRL_SDA_OUT_MASK;
            continue; /* give up and try again. */
        }

        /* try to set sda=0. */
        CLOCK_DelayUs(10u);
        I2Cx->CTRL &= ~I2C_CTRL_SDA_OUT_MASK;
        CLOCK_DelayUs(10u);
        if (I2Cx->CTRL & I2C_CTRL_SDA_MASK)
        {
            I2Cx->CTRL |= I2C_CTRL_SCL_OUT_MASK | I2C_CTRL_SDA_OUT_MASK;
            continue; /* give up and try again. */
        }

        /* try to set sda=1 */
        CLOCK_DelayUs(10u);
        I2Cx->CTRL |= I2C_CTRL_SDA_OUT_MASK;
        CLOCK_DelayUs(10u);
        if (! (I2Cx->CTRL & I2C_CTRL_SDA_MASK))
        {
            I2Cx->CTRL |= I2C_CTRL_SCL_OUT_MASK | I2C_CTRL_SDA_OUT_MASK;
            continue; // Give up and try again
        }

        /* try to set scl=1 */
        CLOCK_DelayUs(10);
        I2Cx->CTRL |= I2C_CTRL_SCL_OUT_MASK;
        CLOCK_DelayUs(10);
        if (I2Cx->CTRL & I2C_CTRL_SCL_MASK)
        {
            err = true; // We have control
            break;
        }
    }

    if (!ctrl_bb)
    {
        I2Cx->CTRL &= ~I2C_CTRL_BB_MODE_MASK;
    }

    /* disable the i2c module, and need to enable later. */
    I2Cx->CTRL &= ~I2C_CTRL_EN_MASK;

    return err;

}

void I2C_ClearRxFifo(I2C_Type *I2Cx)
{
    I2Cx->RXCTRL0 |= I2C_RXCTRL0_FLUSH_MASK;
}

void I2C_ClearTxFifo(I2C_Type *I2Cx)
{
    I2Cx->TXCTRL0 |= I2C_TXCTRL0_FLUSH_MASK;
}

void I2C_SetTxThreshold(I2C_Type *I2Cx, uint32_t num)
{
    I2Cx->TXCTRL0 = (I2Cx->TXCTRL0 & ~I2C_TXCTRL0_THD_VAL_MASK)
                   | (num << I2C_TXCTRL0_THD_VAL_SHIFT);
}

void I2C_SetRxThreshold(I2C_Type *I2Cx, uint32_t num)
{
    I2Cx->RXCTRL0 = (I2Cx->RXCTRL0 & ~I2C_RXCTRL0_THD_LVL_MASK)
                   | (num << I2C_RXCTRL0_THD_LVL_SHIFT);
}

void I2C_SetCommFreq(I2C_Type *I2Cx, uint32_t freq_hz, uint32_t clksrc_hz)
{
    /* Calculate the period of SCL, 50% duty cycle. */
    uint32_t div = ((clksrc_hz / freq_hz) >> 2) - 1u;
    I2Cx->CLKLO = div;
    I2Cx->CLKHI = div;
}

void I2C_Flush(I2C_Type * I2Cx)
{
    /* clear the interrupt flags. */
    I2Cx->INTFL0 = I2Cx->INTFL0;
    I2Cx->INTFL1 = I2Cx->INTFL1;

    /* clear the fifo. */
    I2Cx->TXCTRL0 |= I2C_TXCTRL0_FLUSH_MASK; while (I2Cx->TXCTRL0 & I2C_TXCTRL0_FLUSH_MASK) {}
    I2Cx->RXCTRL0 |= I2C_RXCTRL0_FLUSH_MASK; while (I2Cx->RXCTRL0 & I2C_RXCTRL0_FLUSH_MASK) {}
}

/* 7-bit dev addr. */
bool I2C_WriteOneReg(I2C_Type *I2Cx, uint8_t dev_addr, uint8_t reg_addr, uint8_t reg_val)
{
    I2C_Flush(I2Cx);

    I2Cx->FIFO = dev_addr << 1u;
    I2Cx->MSTCTRL |= I2C_MSTCTRL_START_MASK;
    I2Cx->FIFO = reg_addr;
    I2Cx->FIFO = reg_val;
    I2Cx->MSTCTRL |= I2C_MSTCTRL_STOP_MASK;

    while (!(I2Cx->INTFL0 & I2C_INTFL0_STOP_MASK)) {}
    I2Cx->INTFL0 = I2C_INTFL0_STOP_MASK;

    return ((I2Cx->INTFL0 & I2C_INTFL0_ERRS_MASK) == 0u);
}

bool I2C_WriteMultiRegs(I2C_Type *I2Cx, uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_vals, uint8_t reg_cnt)
{
    I2C_Flush(I2Cx);

    I2Cx->FIFO = dev_addr << 1u;
    I2Cx->MSTCTRL |= I2C_MSTCTRL_START_MASK;
    I2Cx->FIFO = reg_addr;

    while (reg_cnt--)
    {
        I2Cx->FIFO = *reg_vals++;
    }
    I2Cx->MSTCTRL |= I2C_MSTCTRL_STOP_MASK;

    while (!(I2Cx->INTFL0 & I2C_INTFL0_STOP_MASK)) {}
    I2Cx->INTFL0 = I2C_INTFL0_STOP_MASK;

    return ((I2Cx->INTFL0 & I2C_INTFL0_ERRS_MASK) == 0u);
}

bool I2C_ReadOneReg(I2C_Type *I2Cx, uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_val)
{
    I2C_Flush(I2Cx);

    I2Cx->FIFO = dev_addr << 1u;
    I2Cx->MSTCTRL = I2C_MSTCTRL_START_MASK;
    I2Cx->FIFO = reg_addr;

    I2Cx->RXCTRL1 = 1u; /* rx count. */
    I2Cx->MSTCTRL = I2C_MSTCTRL_RESTART_MASK; /* prepare to read. */
    while (I2Cx->MSTCTRL & I2C_MSTCTRL_RESTART_MASK)
    {}
    I2Cx->FIFO = (dev_addr << 1u) | 0x01;

    while (!(I2Cx->INTFL0 & I2C_INTFL0_RX_THD_MASK))
    {}
    *reg_val = I2Cx->FIFO;
    I2Cx->INTFL0 = I2C_INTFL0_RX_THD_MASK;

    /* send stop condition. */
    I2Cx->MSTCTRL = I2C_MSTCTRL_STOP_MASK;
    while (!(I2Cx->INTFL0 & I2C_INTFL0_STOP_MASK))
    {}
    I2Cx->INTFL0 = I2C_INTFL0_STOP_MASK;

    return ((I2Cx->INTFL0 & I2C_INTFL0_ERRS_MASK) == 0u);
}

/* EOF. */

